docs(objectql): search-companion's $search sentence says $icontains, and the __search reader is named expandSearchToFilter - #14594
Conversation
…is `$icontains`, and the `__search` reader is `expandSearchToFilter` Two independent comment-only corrections in `packages/objectql/src/search-companion.ts`, zero behaviour: 1. The module docblock opened "`$search` (ADR-0061 Tier 1) is a `$contains` over source columns". Source-column clauses compile to `$icontains` (`search-filter.ts:109` / `:111`), adjudicated at `search-filter.ts:23`: "[#7641] The case-insensitive operator is `$icontains`, NOT `$contains`." Operator spelling only — the argument the sentence carries survives the correction unchanged (case folding does not transliterate, so `zhangwei` cannot hit a stored CJK original under either operator). 2. `provisionSearchCompanion`'s docblock named the companion column's only reader `buildSearchFilter`, which is not an export of `search-filter.ts`. The reader is `expandSearchToFilter`. The two remaining `$contains` in this file (`:237`, `:286`) describe the hidden `__search` companion clause, where the case-SENSITIVE operator is correct and deliberate (`search-filter.ts:137-143`, "Do not 'align' the two"). Untouched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
📓 Docs Drift Check
What this run could not see
Coarse fallback — 16 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
|
Landing provenance (engine execution seat,
Generated by Claude Code |
Fixes #13984
Comment-only, zero behaviour. Two lines of
packages/objectql/src/search-companion.tschange, plus a changeset. The two corrections are distinct claims and are listed separately, as triage asked.1. The module docblock's operator spelling (
:6)The opening sentence read:
The source-column clauses compile to
$icontains. Measured on this branch:packages/objectql/src/search-filter.ts:101—fieldClausesForTerm, returning[{ [field]: { $icontains: term } }]at:109(the enum raw-value fallback) and:111(the textual path).:23:[#7641] The case-insensitive operator is$icontains, NOT$contains.Only the operator spelling changed. The argument the sentence carries is untouched and still holds: typing
zhangweicannot hit a stored张伟under EITHER operator, because case folding does not transliterate. It is a false spelling inside a correct argument, which is precisely why it read as fine and survived.Every line number the card cites was verified against this tree with no drift:
search-filter.ts:23,:109,:111,:137-143are all exactly where the card puts them.2. A stale identifier in the same docblock family (
:286)provisionSearchCompanion's docblock named the companion column's only readerbuildSearchFilter. That is not an export ofsearch-filter.ts, nor of anything else in the repo —git grep buildSearchFilteroverpackagesandappshits only this one line plus two frozen CHANGELOG copies of it. The reader isexpandSearchToFilter, the name this same file already uses correctly 50 lines above at:236.Deliberately NOT touched: two
$containsin this file are CORRECTDo NOT batch-replace this file — two
$containsin it are CORRECT.search-companion.ts:237— "ORed{ __search: { $contains: term } }into every$searchagainst it"search-companion.ts:286— "which emits{ __search: { $contains: term } }"Both describe the hidden
__searchcompanion clause, where the case-SENSITIVE operator is the right spelling and is deliberate: the column is a normalized blob already lowercase on both sides, so$containsover two folded values is exact, not a case bug.search-filter.ts:137-143says so in the imperative — "Do not 'align' the two." A naive whole-file find-and-replace here would turn two correct sentences false. Only:6was wrong.Verified after the edit:
grep -c '__search: { $contains: term }'still returns 2, andgrep -c 'buildSearchFilter'returns 0.Measurement: does either correction reach
dist/*.d.ts?After
pnpm --filter '@objectstack/objectql...' build, grepping all six emitted declaration files:core.d.ts/.d.mtsindex.d.ts/.d.mtsutil-C5RtxP_I.d.ts/.d.mtsexpandSearchToFilter"SEARCH_COMPANION_FIELDprovisionSearchCompanionThe positive controls are what make the zeros readable: JSDoc attached to an exported declaration does reach the emitted declarations here, so a 0 on the module docblock is a real absence and not a broken grep.
importis dropped by the declaration emitter, as the dispatch hypothesised.packages/objectql/dist/util-C5RtxP_I.d.ts:1466(and the.d.mtstwin), because it sits inside the JSDoc of the exportedprovisionSearchCompanion.Changeset decision, and a declared deviation
.changeset/search-companion-docblock.md—@objectstack/objectqlpatch. Noskip-changesetlabel.The dispatch's letter was: a patch changeset only if the MODULE docblock reaches
dist/*.d.ts, elseskip-changeset. The module docblock does not reach it. But the measurement turned up a fact that clause did not anticipate: correction 2 does reach the shipped declarations, so the npm tarball's bytes change and a consumer's editor hover onprovisionSearchCompanionstops naming a function that does not exist. Theskip-changesetcriterion is "publishes nothing from any package", which this diff fails. The precedent is.changeset/action-execute-ledger-dead.md, a data-only patch changeset taken for exactly this reason: "these ledgers ship in the npm tarball and this is published data."Taking the changeset is also the safe direction — it is additive, whereas applying
skip-changesetto a diff that does change published bytes would silence a gate that ought to speak. Raised here as an explicit deviation from the letter of the dispatch clause rather than taken silently.Clause-②: no
Read from my own diff rather than from the dispatch: both changed lines sit inside
/** ... */comment blocks. No accept/reject behaviour, no schema, no public type, no gate moves. The emitted declaration's doc text changes atutil-C5RtxP_I.d.ts:1466; the declared types are byte-identical.The same correction campaign covers three surfaces, on three separate cards
Named here so the three do not drift into three different wordings. Both of the others remain open, keep their own review path, and are untouched by this branch, which edits one file.
$orof$contains— the implementation and its own neighbor file say$icontains#13744 —packages/objectql/src/engine.ts, the origin card this finding was filed from.$orof$containsin six places — and one of them tells the reader the case-insensitivity question is "still open" when #7641 closed it #13988 — thecontent/docs/**sites plus the spec.describe()generation source. That one reachespackages/specand therefore lights Clause-②; this one does not.Verification
Everything below ran on
ced1f11cf(git rev-parse --short HEAD, taken after the final commit).The gate union was re-derived on this final tree, not taken from the dispatch seed:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat commitced1f11cfreturned 30 commands over the 2-file change set. All 30 ran, pluspnpm check:adr-anchors(named by the dispatch but not in the derived union), plus the always-runs. Every exit code was captured after a redirect to a file, never through a pipe.Green — 27 of the 30 derived, and every always-run:
pnpm lint(whole repo,eslint . --no-inline-config) — exit 0, no findings. Not narrowed: the full repo scan ran.pnpm check:nul-bytes— "check-nul-bytes: OK (scanned 7967 text file(s) ... no raw ASCII control bytes)."pnpm check:error-status-conformance— "every derivable runtime status is documented, and every documented status is reachable."pnpm --filter @objectstack/objectql typecheck— exit 0, and it carriescheck:test-typecheck: "OK — @objectstack/objectql's test layer compiles under packages/objectql/tsconfig.test.json". So the test layer is measured, not excluded.[#7641]pins —pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/search-filter.test.ts src/global-search-palette-recall.test.ts→ "Test Files 2 passed (2) / Tests 28 passed (28)". The two named pin titles live in those files atsearch-filter.test.ts:140andglobal-search-palette-recall.test.ts:352.pnpm check:adr-anchors— "check-adr-anchors: OK (53 anchored file(s), every governing ADR still referenced ...)".scripts/adr-anchors/packages__objectql__src__search-companion.ts.jsonstill anchors ADR-0015 / ADR-0045 / ADR-0061 and the correction touched no ADR id, as the card predicted.check-empty-changeset,check-changeset-no-major,check:doc-authoring,check:published-filesandcheck:test-source-alias.NOT MEASURED — 3 of the derived 30 exited 3. In each gate's own words this is neither green nor red:
node scripts/check-test-completeness.mjs— "the local reading for this gate is NOT MEASURED. It is not a red, and there is nothing here to fix." It needs a savedturbo run testlog, which CI tees in on every invocation.node scripts/pm/check-half-states.mjs— "Nothing was swept ... it is no reading at all." It needs repo-scoped REST reads, and those answer 403 in this container.pnpm check:dual-build-cjs-loads— "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ ... This is NOT a pass: nothing was measured." I built only@objectstack/objectqland its dependency closure; the gate wants a whole-repopnpm build, which CI performs. A comment-only diff cannot move CJS load behaviour.No ablation is owed. The diff is comment-only: there is no guard to delete and no assertion whose failure could be demonstrated.
Heavy runs (the build, the pin tests) went through
scripts/pm/os-verify-lock.sh; both printedVERDICT command-exit 0.🤖 Generated with Claude Code
Generated by Claude Code
Generated by Claude Code